# Simple content providers for APKN/Plone - No viewlets
# registered in configure.zcml and accessed from zpt templates
from zope.interface import Interface
from zope.interface import implements
from zope.component import adapts
from zope.contentprovider.interfaces import IContentProvider
from zope.publisher.interfaces.browser import IDefaultBrowserLayer
from zope.app.pagetemplate import ViewPageTemplateFile
from Acquisition import aq_inner, aq_parent
from Products.CMFCore.utils import getToolByName
from Products.LinguaPlone.interfaces import ITranslatable
from plonetheme.apkntheme import APKNThemeMessageFactory as _, logger
from plonetheme.apkntheme.settings import ALLOWED_DASHBOARD_ACTIONS_ROOT,\
										  SECTION_TITLES_ROOT,\
										  COUNTRY_CONTEXT_MENU_ROOT, \
										  COUNTRY_FOLDERS_PATH_PROXIED, \
										  THEMATIC_SITES_MENU_ITEMS,\
										  JAVASCRIPT_GLOBALS,\
										  COUNTRY_FOLDER_META,\
										  COUNTRY_RESOURCES_MENU_ITEMS,\
										  SEARCH_BUTTON_PARAM,\
										  SEARCH_TEXT_PARAM,\
										  PARLIAMENTARY_PROFILES_BASE_TYPES,\
										  COUNTRY_CONTACTS_TYPES_BASE,\
										  COUNTRY_RESOURCES_CONTENT_SOURCES,\
										  RESOURCES_SOURCE_TYPE_CATALOG,\
										  RESOURCES_SOURCE_TYPE_RSS,\
										  RESOURCES_RESULTS_LIMIT,\
										  CONTEXT_TITLE_SECTIONS,\
										  COUNTRY_RESOURCES_SORT_KEY_MAPPINGS
from plonetheme.apkntheme.browser import syndication
from plonetheme.apkntheme.utils.tools import get_host_url,\
										generate_random_string
from itertools import groupby

from apkn.resource.interfaces import IResource

try:
	import json
except ImportError:
	import simplejson as json

class TranslationsForObject(object):
	'''
	Get Translation Data for Items
	'''
	implements(IContentProvider)
	adapts(Interface, IDefaultBrowserLayer)
	
	def __init__(self, context, request, view):
		self.context = context
		self.request = request
		self.__parent__ = view

	def update(self):
		self.apknLangs = self.getTranslationsForItem()
	
	def getTranslationsForItem(self):
		'''
		Return a list of translations and languages with no
		translations {'translations':list of dicts, 
		notranslations:list}
		'''
		translations = {'translations':[], 'untranslated': [],'lang':'auto'}
		obj = aq_inner(self.context)
		if ITranslatable.providedBy(obj):
			translations['lang'] = obj.getLanguage()
			lang_tool = getToolByName(self.context, u'portal_languages')
			if lang_tool:
				lang_codes = lang_tool.supported_langs
				translated_langs = lang_codes
				translations['all_langs'] = lang_codes
				for code in lang_codes:
					translation = obj.getTranslation(code)
					if translation:
						translated_langs.remove(code)
						translations['translations'].append(translation)
					else:
						translations['untranslated'].append(code)
			translations['all_langs'] = ','.join(translations['all_langs'])
			translations['untranslated'] = ','.join(translations['untranslated'])
		return translations
	
	#render method of our content provider
	render = ViewPageTemplateFile('templates/lang.pt')

class ContextTitleProvider(object):
	'''
	Renders the context Title for Particular content types
	This is useful whenever the hierarchy is too deep
	'''
	def update(self):
		pass
	
	def __init__(self, context, request, view):
		self.context = context
		self.request = request
		self.__parent = view

	def showContextTitle(self):
		context_path = '/'.join(self.context.getPhysicalPath())
		context_meta_type = self.context.Type()
		title_depth = 0
		for context_title_spec in CONTEXT_TITLE_SECTIONS:
			if context_meta_type in context_title_spec[u'types'] and\
			   context_title_spec[u'path'] in context_path:
				title_depth = context_title_spec[u'depth']
				break
		return title_depth
	
	def getContextTitle(self):
		last_title_type = u"ATFolder"
		proceed = True
		title_components = []
		show_depth = self.showContextTitle()
		if not show_depth:
			return None
		current_object = aq_inner(self.context)
		while proceed:
			current_object = aq_parent(current_object)
			if current_object.meta_type == last_title_type:
				show_depth = show_depth - 1
			if (show_depth <= 0):
				proceed = False
			the_object_title = unicode(current_object.Title(), "UTF-8")
			title_components.append(the_object_title)
		title_components.reverse()
		return u' | '.join(title_components)
	
	render = ViewPageTemplateFile("templates/context_title_provider.pt")

class CountryMenuProvider(object):
	'''
	Provides data for country menu items as portlet items
	'''
	def __init__(self, context, request, view):
		self.context = context
		self.request = request
		self.__parent__ = view
		
	def update (self):
		self.context_menu = self.getContextMenu()
		self.context_title = self.getContextMenu(get_title = True)

	def getContextMenu(self, get_title = False):
		'''
		Gets the menu items for country folder content types with
		the option to get a link to a particular item and optional
		display based on user login status.
		See: COUNTRY_CONTEXT_MENU defined at the top of this module
		'''
		current_object = None
		path_object = None
		menu_items = []
		from plonetheme.apkntheme.settings import \
		COUNTRY_FOLDERS_PATH, COUNTRY_CONTEXT_MENU
		object_path = '/'.join(self.context.getPhysicalPath())
		if COUNTRY_FOLDERS_PATH in object_path:
			path_object = object_path.replace(COUNTRY_FOLDERS_PATH, '')
		if path_object:
			try:
				country_name = path_object.split('/')[0]
			except Exception, e:
				country_name = None
				logger.error("Cound not load country folder: Error %s", 
				e.__str__())
			if country_name:
				country_path = COUNTRY_FOLDERS_PATH + country_name
				try:
					country_folder =\
					 self.context.restrictedTraverse(country_path)
				except Exception, e:
					country_folder = None
					logger.error("Traversal to country\
					 folder failed with error %s", e.__str__())
				if country_folder:
					current_object = country_folder
		else:
			#the portlet is rendered from paths outside country directory
			#try to extract country object from request
			country_in_request = self.request.form.get('country')
			if country_in_request:
				catalog = getToolByName(self.context, 
											'portal_catalog')
				search_folders = \
				catalog.searchResults(Type='Folder', 
										path=COUNTRY_FOLDERS_PATH,
										Title=country_in_request)
				if search_folders:
					first_result = search_folders[0].getObject()
					if hasattr(first_result, COUNTRY_FOLDER_META):
						current_object = first_result
		if current_object:
			CONTEXT_TITLE = current_object.Title()
			if get_title:
				return CONTEXT_TITLE
			CONTEXT_ID = current_object.id
			CONTEXT_URL = current_object.absolute_url()
			COUNTRY_CONTEXT_CODE = getattr(current_object, 
											COUNTRY_FOLDER_META,
											'')
			if COUNTRY_CONTEXT_CODE:
				COUNTRY_CONTEXT_CODE += '.'
			for cmenu in COUNTRY_CONTEXT_MENU:
				link = cmenu[1]
				show = cmenu[3]
				if (show and self.isUserAnonymous()):
					if link.startswith(u'?'):
						link = "%s%s" %(CONTEXT_URL, link)
					link = link.replace(u'@@CONTEXT_ID', CONTEXT_ID)
					link = link.replace(u'@@CONTEXT_TITLE', CONTEXT_TITLE)
					link = link.replace(u'@@CONTEXT_COUNTRY_CODE', COUNTRY_CONTEXT_CODE)
					
					if cmenu[2]:
						content_items = self.getSiblingsOfType(cmenu[2], 
						current_object)
						if len(content_items) == 1:
							link = content_items[0][1]
					
					menu_items.append({'title': cmenu[0], 'link': link})
		return menu_items

	def isUserAnonymous(self):
		pm = getToolByName(self.context, 'portal_membership')
		anon = pm.isAnonymousUser()
		return anon

	def getSiblingsOfType(self, meta_type, container):
		from plonetheme.apkntheme.settings import RECENT_ITEM_COUNT
		item_list = []
		object_ids = container.objectIds()
		for oid in object_ids:
			obj = getattr(container, oid)
			if obj.portal_type in meta_type:
				title = obj.Title()
				link = obj.absolute_url()
				item_list.append((title, link))
			if len(item_list) >= RECENT_ITEM_COUNT:
				break
		return item_list

	render = ViewPageTemplateFile(u'templates/country_folder_menu.pt')


class CountryContainerContextMenuProvider(object):
	'''
	Render country context menu in Portlet
	'''
	implements(IContentProvider)
	adapts(Interface, IDefaultBrowserLayer)
	
	def __init__(self, context, request, view):
		self.context = context
		self.request = request
		self.__parent__ = view
	
	def update(self):
		self.context_menu = self.getContextMenu()
		
	def getContextMenu(self):
		items = []
		path_info = self.request.environ.get('PATH_INFO', '')
		for item in ALLOWED_DASHBOARD_ACTIONS_ROOT:
			isActive = ''
			link = "?go=%s" %(item)
			link = COUNTRY_CONTEXT_MENU_ROOT[item][2]
			if link.startswith("?"):
				link = "%s%s" %(COUNTRY_FOLDERS_PATH_PROXIED, link)
			title = COUNTRY_CONTEXT_MENU_ROOT[item][1]
			if link in path_info:
				isActive = u'navTreeCurrentItem'
			#logger.info("Testing path [%s] context for active status with link [%s] and found: %s", 
			#path_info, link, isActive)
			lk_data = {'link': link, 'title': title, 'active': isActive}
			items.append(lk_data)
		return items

	render = ViewPageTemplateFile("templates/country_container_menu.pt")

class BaseMenuProvider(object):
	'''
	Returns a dictionary of menu items for display to a target template
	'''
	implements(IContentProvider)
	adapts(Interface, IDefaultBrowserLayer)

	def __init__(self, context, request, view):
		self.context = context
		self.request = request
		self.__parent = view

	def update(self):
		self.menu_items = self.getMenuItems()
		self.menu_title = self.getMenuTitle()
		
	
	def getMenuTitle(self):
		return _('menu title')
	
	def getMenuItems(self):
		'''
		Override this with your own menu format and structure
		'''
		return []
		
	render = ViewPageTemplateFile("templates/blank_menu.pt")

class ThematicSitesContentProvider(BaseMenuProvider):
	'''
	render thematic content links on apkn - via a classic portlet
	'''	
	
	def getMenuTitle(self):
		return _(u"APKN portals")
	
	def getMenuItems(self):
		thematic_sites = []
		for item in THEMATIC_SITES_MENU_ITEMS:
			thematic_sites.append({'id': item[0],
			'text': item[1],
			'title': item[2],
			'link': item[3]})
		return thematic_sites
	render = ViewPageTemplateFile("templates/thematic_sites_menu.pt")

class CountryResourcesMenuProvider(BaseMenuProvider):
	'''
	Provides content for country resources menu
	'''
	def getMenuTitle(self):
		return _(u"country resources")
		
	def getMenuItems(self):
		thematic_sites = []
		for item in COUNTRY_RESOURCES_MENU_ITEMS:
			thematic_sites.append({'id': item[0],
			'text': item[1],
			'title': item[2],
			'link': item[3]})
		return thematic_sites
	render = ViewPageTemplateFile("templates/thematic_sites_menu.pt")

class ParliamentaryFeedsContentProvider(object):
	'''
	Gets parliamentary feeds markup into document via portlet
	Actual feed loading may be done via python or javascript
	See .syndication.py for implementation details
	'''
	implements(IContentProvider)
	adapts(Interface, IDefaultBrowserLayer)
	
	def __init__(self, context, request, view):
		self.context = context
		self.request = request
		self.__parent = view
	
	def update(self):
		self.feed_data = syndication.buildParliamentaryFeedInfo()
	
	render = ViewPageTemplateFile("templates/apkn_parliament_feeds.pt")

class ParliamentaryFeedsContentProviderOther(ParliamentaryFeedsContentProvider):
	'''
	Renders parliamentary feeds in other locations outside root
	'''
	render = ViewPageTemplateFile("templates/apkn_parliament_feeds_other.pt")

class TWGFeedsContentProvider(object):
	"""
	TWG Feeds Provider
	"""
	implements(IContentProvider)
	adapts(Interface, IDefaultBrowserLayer)
	
	def __init__(self, context, request, view):
		self.context = context
		self.request = request
		self.__parent = view
		self.feed_base_url = get_host_url(self.request)
	
	def update(self):
		self.feed_data = syndication.buildTWGFeedInfo(self.feed_base_url)
		self.feed_info = [self.trans_string(fi['title']) for fi in self.feed_data]
	
	def trans_string(self, text):
		return self.context.translate(text)
	
	render = ViewPageTemplateFile("templates/twg_feeds_portlet.pt")
	

class TranslationsControlProvider(object):
	"""
	Return a simple list of available translations for a document
	"""
	def __init__(self, context, request, view):
		self.context = context
		self.request = request
		self.__parent = view
		self.__translation_message = _('in')
		self.translations = self.getTranslations()
		
	def update(self):
		self.translations = self.getTranslations()
	
	def getLanguageName(self, language):
		lang_tool = getToolByName(self.context, u'portal_languages')
		available_languages = lang_tool.getAvailableLanguages()
		language_name = available_languages[language][u'native']
		return language_name
	
	def getLanguage(self):
		return self.context.getLanguage()
	
	def getTransMessage(self, target_language):
		return self.context.translate(self.__translation_message, 
									target_language = target_language)
									
	def getTranslations(self):
		translations = {}
		try:
			current_object = aq_inner(self.context)
		except:
			current_object = None
		if current_object:
			if ITranslatable.providedBy(current_object):
				#locate translations
				translations = current_object.getTranslations()
		filtered_translations = \
			filter(lambda t:t[0] != self.getLanguage(), translations.items())
		
		final_translations = [
			{
			'language_code': trans[0],
			'translation': trans[1][0],
			'published': trans[1][1] == u'published',
			'language_name': self.getLanguageName(trans[0]),
			'translation_message': self.getTransMessage(trans[0])
			}
			for trans in filtered_translations
		]
		final_translations_sorted = sorted(final_translations, 
									key = lambda t: t['language_code'])
		return final_translations_sorted
		
	render = ViewPageTemplateFile("templates/translations_control.pt")


class JSGlobalsProvider(object):
	'''
	Loads APKN-specific JavaScript globals into the html document
	'''
	def __init__(self, context, request, view):
		self.context = context
		self.request = request
		self.__parent = view
	
	def update(self):
		self.js_variables = "window['apkn_globals'] = %s;" \
		%(self.getJavaScriptGlobalsAsJSON())
	
	def getJavaScriptGlobalsAsJSON(self):
		all_js_variables = [(name,value) 
		for name,value in JAVASCRIPT_GLOBALS]
		js_variable_dict = {}
		for jvar in all_js_variables:
			final_var = jvar[1]
			if hasattr(jvar[1], u'translate'):
				final_var = self.context.translate(final_var)
			js_variable_dict[jvar[0]] = final_var
		return json.dumps(js_variable_dict)
		
	render = ViewPageTemplateFile("templates/apkn_js_globals.pt")

class LegislativeGuidelinesHeaderProvider(object):
	'''
	Applies custom styling to legislative guideline documents
	'''
	def __init__(self, context, request, view):
		self.context = context
		self.request = request
		self.__parent = view
	
	def update(self):
		self.banner_image = self.getBannerImage()
		
	def getBannerImage(self):
		return u"Drafting_guidelines_small_%s.png" \
			% (self.getContextLanguage())
	
	def getStyleClass(self):
		return u'bgLegWhite'
	
	def getContextLanguage(self):
		context_language = 'en'
		portal_languages = getToolByName(self.context, 
													'portal_languages')
		if portal_languages:
			context_language = portal_languages.getLanguageBindings()[0]

		return context_language
	render = ViewPageTemplateFile("templates/legislative_guidelines_header.pt")

class LegislativeGuidelinesHeaderInnerProvider(LegislativeGuidelinesHeaderProvider):
	def getBannerImage(self):
		return u"Drafting_guidelines_big_%s.png" \
			% (self.getContextLanguage())
		
	def getStyleClass(self):
		return u'bgLegRed'


class BaseContextSearchProvider(object):
	'''
	Builds a search form for a context with customized search
	parameters
	'''
	implements(IContentProvider)
	adapts(Interface, IDefaultBrowserLayer)

	def __init__(self, context, request, view):
		self.context = context
		self.request = request
		self.__parent = view

	def update(self):
		self.search_title = self.getSearchTitle()
	
	def verboseObjectPath(self, the_object):
		return unicode(the_object.Title(), "UTF-8")
	
	def buildSearchQuery(self):
		query = {}
		query['path']  = self.getSearchPath()
		query['SearchableText'] = self.getSearchText()
		return query
	
	def extendSearchQuery(self):
		'''
		Extends the search query with other parameters
		'''
		return self.buildSearchQuery()
		
	def getSearchPath(self):
		return '/'.join(self.context.getPhysicalPath())
	
	def searchSubmitted(self):
		return self.request.form.get(SEARCH_BUTTON_PARAM, None)
	
	def searchResults(self):
		'''
		Returns ZBrains of context based on the search query
		'''
		catalog_tool  = self.context.portal_catalog
		
		if catalog_tool:
			search_results = \
			catalog_tool.searchResults(self.extendSearchQuery()) 
		search_results = [sr.getObject() for sr in search_results]
		search_results_info = {u'search_results': search_results,
		u'search_message': self.getSearchMessage(len(search_results))}
		return search_results_info
	
	def getSearchText(self):
		return self.request.form.get(SEARCH_TEXT_PARAM, '')
	
	def getSearchMessage(self, result_count):
		return _(u'context_search_results_message',
				default = u"You searched for \"${search_text}\". \
				${search_count} found",
				mapping = {u'search_text': self.getSearchText(),
				u'search_count': result_count})
	
	def getSearchTitle(self):
		return _('search')
	
	def getResultTitle(self, search_object):
		result_title = unicode(search_object.Description(), "UTF-8")
		if not result_title:
			result_title = unicode(search_object.Title(), "UTF-8")
		return result_title
	
	render = ViewPageTemplateFile("templates/context_search.pt")


class ParliamentaryProfilesSearch(BaseContextSearchProvider):
	def extendSearchQuery(self):
		query = self.buildSearchQuery()
		query['Type'] = PARLIAMENTARY_PROFILES_BASE_TYPES
		return query

	def verboseObjectPath(self, the_object):
		context_path = u'/'.join(self.context.getPhysicalPath())
		object_path = u'/'.join(the_object.getPhysicalPath())
		inner_path = object_path.replace(context_path, '').split(u'/')
		inner_path.remove('')
		final_title = unicode(the_object.Title(), "UTF-8")
		if inner_path:
			final_title = ' - '.join([final_title, 
									inner_path[0].title()])
		return final_title


class ParliamentaryContactsSearch(BaseContextSearchProvider):
	def extendSearchQuery(self):
		query = self.buildSearchQuery()
		query[u'Type'] =COUNTRY_CONTACTS_TYPES_BASE
		return query

	def verboseObjectPath(self, the_object):
		context_path = u'/'.join(self.context.getPhysicalPath())
		object_path = u'/'.join(the_object.getPhysicalPath())
		inner_path = object_path.replace(context_path, '').split(u'/')
		inner_path.remove('')
		final_title = unicode(the_object.Title(), "UTF-8")
		if inner_path:
			final_title = ' - '.join([final_title, 
									inner_path[0].title()])
		return final_title


class ResourceContextSearch(BaseContextSearchProvider):
	def extendSearchQuery(self):
		query = self.buildSearchQuery()
		query[u'object_provides'] = IResource.__identifier__
		return query


class CountryResourcesContentProvider(object):
	'''
	Renders content for country folders from various sources
	'''
	implements(IContentProvider)
	adapts(Interface, IDefaultBrowserLayer)
	
	def __init__(self, context, request, view):
		self.context = context
		self.request = request
		self.__parent = view
	
	def update(self):
		pass
	
	def processSource(self, source_def, source_url):
		if source_def.get(u'source_type', None) is\
		 RESOURCES_SOURCE_TYPE_CATALOG:
			 return self.processSourceCatalog(source_def, source_url)
		return []
	
	def processSourceCatalog(self, source_def, source_url):
		catalog = getToolByName(self.context, u'portal_catalog')
		if not catalog:
			return []
		query = {}
		query[u'path'] = source_url
		query[u'sort_limit'] = RESOURCES_RESULTS_LIMIT 
		query[u'sort_on'] = u'Date'
		query[u'sort_order'] = u'descending'
		for index, param in \
			source_def.get(u'extra_search_query', {}).iteritems():
			if str(param).startswith(u'@@META'):
				param_name = param.split(':')[1]
				param = unicode(self.context.get(param_name), "UTF-8")
			query[index] = param
		logger.info("Performing Query: %s", query)
		return catalog.searchResults(query)[:RESOURCES_RESULTS_LIMIT]

	def getSortKeyTitle(self, group_on, key):
		'''
		Extract Title from Vocabularies
		'''
		vocabulary = COUNTRY_RESOURCES_SORT_KEY_MAPPINGS.get(group_on, 
																None)
		if vocabulary:
			return vocabulary.getValue(key)
		else:
			return key

	def groupBrains(self, brain_list, group_on):
		'''
		Group the results of a catalog search operation
		For now this uses resourcesubject
		'''
		grouped_brain_list = []
		for key, group in groupby(brain_list,
								  lambda brain: brain.resourcesubject[0]):
			group_data = {}
			group_data[u'section_title'] = self.getSortKeyTitle(group_on, key)
			group_data[u'section_items'] = []
			for item in group:
				group_data[u'section_items'].append(item)
			grouped_brain_list.append(group_data)
		return grouped_brain_list
	
	def processSources(self):
		'''
		Return country content with a common structure:
		* content_title
		* content_listing
		* content_url
		'''
		content_parts = []
		for resource_content in COUNTRY_RESOURCES_CONTENT_SOURCES:
			resource_spec = {}
			resource_spec[u'title'] = resource_content[u'title']
			resource_spec[u'verbose_title'] = resource_content[u'verbose_title']
			resource_spec[u'group_on'] = resource_content.get(u'group_on', False)
			source_url = resource_content.get(u'source_url')
			if not source_url:
				source_url = \
					'/'.join(self.context.getPhysicalPath())
			resource_spec[u'content_listing'] = \
			self.processSource(resource_content, source_url)
			
			if resource_spec[u'group_on']:
				resource_spec[u'content_listing'] = \
				self.groupBrains(resource_spec[u'content_listing'],
								  resource_spec[u'group_on'])
			
			resource_spec[u'has_content'] = \
				bool(len(resource_spec[u'content_listing']))
			resource_spec[u'dom_id'] = generate_random_string(10)
			context_id = self.context.getId()
			if not resource_content.get(u'final_url'):
				search_space = \
				self.context.restrictedTraverse(str(resource_content.get('source_url')))
				if search_space:
					resource_spec[u'final_url'] = search_space.absolute_url()
					if resource_content.get(u'final_url_parts'):
						url_parts = resource_content.get(u'final_url_parts')
						url_parts = url_parts.replace('@@CONTEXT_ID', context_id)
						resource_spec['final_url'] = '?'.join((
						resource_spec[u'final_url'],
						url_parts
						))
			else:
				resource_spec[u'final_url'] = \
				resource_content[u'final_url'].replace('@@CONTEXT_ID', context_id)
			content_parts.append(resource_spec)
		return content_parts

	def verboseObjectTitle(self, the_object):
		context_path = u'/'.join(self.context.aq_parent.getPhysicalPath())
		object_path = u'/'.join(the_object.getPhysicalPath())
		inner_path = object_path.replace(context_path, '').split(u'/')
		inner_path.remove('')
		final_title = unicode(the_object.Title(), "UTF-8")
		if inner_path:
			final_title = ' - '.join([final_title, 
									inner_path[0].title()])
		return final_title


	render = ViewPageTemplateFile("templates/country_resources_content.pt")
